package service

import (
	"context"
	"fmt"
	"github.com/toolkits/pkg/logger"
	appsV1 "k8s.io/api/apps/v1"
	coreV1 "k8s.io/api/core/v1"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

func GetDeploymentList(clientSet *kubernetes.Clientset, namespaceName string) (deploymentList *appsV1.DeploymentList, err error) {
	deploymentList, err = clientSet.AppsV1().Deployments(namespaceName).List(context.TODO(), metaV1.ListOptions{})
	if err != nil {
		return nil, err
	}
	return deploymentList, err
}

func GetDeployment(clientSet *kubernetes.Clientset, namespaceName string, deploymentName string) (deploymentInfo *appsV1.Deployment, err error) {
	deploymentInfo, err = clientSet.AppsV1().Deployments(namespaceName).Get(context.TODO(), deploymentName, metaV1.GetOptions{})
	if err != nil {
		return nil, err
	}
	return deploymentInfo, err
}

func CreateDeployment(clientSet *kubernetes.Clientset, request CreateDeploymentRequest) (deploymentInfo *appsV1.Deployment, err error) {

	containerPort := explainPort(request.PortsNum)
	//这个结构和原生k8s启动deployment的yml文件结构完全一样，对着写就好
	deployment := &appsV1.Deployment{
		ObjectMeta: metaV1.ObjectMeta{
			Name: request.DeploymentName,
		},
		Spec: appsV1.DeploymentSpec{
			Replicas: &request.Replicas,
			Selector: &metaV1.LabelSelector{
				MatchLabels: map[string]string{
					"app": request.DeploymentName,
				},
			},
			Template: coreV1.PodTemplateSpec{
				ObjectMeta: metaV1.ObjectMeta{
					Labels: map[string]string{
						"app": request.DeploymentName,
					},
				},
				Spec: coreV1.PodSpec{
					Containers: []coreV1.Container{
						{
							Name:  request.DeploymentName,
							Image: request.Image,
							Ports: containerPort,
						},
					},
				},
			},
		},
	}
	//挂载
	volumeMounts, volumes := explainVolumeMounts(request.MountConfigmapInfos, request.MountPvcInfos, request.HostPathInfos)
	deployment.Spec.Template.Spec.Volumes = volumes
	deployment.Spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts
	//secret
	if len(request.ImagePullSecrets) != 0 {
		imagePullSecrets := make([]coreV1.LocalObjectReference, 10)
		for i, secretName := range request.ImagePullSecrets {
			imagePullSecrets[i].Name = secretName
			logger.Infof("%d: %s", i, secretName)
		}
		deployment.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets
	}

	//创建
	deploymentInfo, err = clientSet.AppsV1().Deployments(request.NamespaceName).Create(context.TODO(), deployment, metaV1.CreateOptions{})
	if err != nil {
		return deploymentInfo, err
	}
	return deploymentInfo, nil
}

func explainPort(portList []int32) (response []coreV1.ContainerPort) {
	for _, protNum := range portList {
		containerPort := coreV1.ContainerPort{
			ContainerPort: protNum,
		}
		response = append(response, containerPort)
	}
	return response
}

func DeleteDeployment(clientSet *kubernetes.Clientset, request DeleteDeploymentRequest) (err error) {
	err = clientSet.AppsV1().Deployments(request.NamespaceName).Delete(context.TODO(), request.DeploymentName, metaV1.DeleteOptions{})
	if err != nil {
		return err
	}
	return nil
}

func ApplyDeployment(clientSet *kubernetes.Clientset, request ApplyDeploymentRequest) (deploymentInfo *appsV1.Deployment, err error) {

	deployment, err := clientSet.AppsV1().Deployments(request.NamespaceName).Get(context.TODO(), request.DeploymentName, metaV1.GetOptions{})
	if err != nil {
		return deploymentInfo, err
	}

	if len(request.Image) != 0 {
		deployment.Spec.Template.Spec.Containers[0].Image = request.Image
	}
	if request.Replicas != 0 {
		deployment.Spec.Replicas = &request.Replicas
	}
	if len(request.Labels) != 0 {
		//deployment.Spec.Selector.MatchLabels = request.Labels
		deployment.Spec.Template.ObjectMeta.Labels = request.Labels
	}

	if len(request.ImagePullSecrets) != 0 {
		imagePullSecrets := make([]coreV1.LocalObjectReference, 10)
		for i, secretName := range request.ImagePullSecrets {
			imagePullSecrets[i].Name = secretName
			logger.Infof("%d: %s", i, secretName)
		}
		deployment.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets
	}
	deploymentInfo, err = clientSet.AppsV1().Deployments(request.NamespaceName).Update(context.TODO(), deployment, metaV1.UpdateOptions{})
	if err != nil {
		return deploymentInfo, err
	}
	return deploymentInfo, nil
}

func MountVolumes(clientSet *kubernetes.Clientset, request MountForDeploymentRequest) (deploymentInfo *appsV1.Deployment, err error) {

	deployment, err := clientSet.AppsV1().Deployments(request.NamespaceName).Get(context.TODO(), request.DeploymentName, metaV1.GetOptions{})
	if err != nil {
		return deploymentInfo, err
	}

	volumeMounts, volumes := explainVolumeMounts(request.MountConfigmapInfos, request.MountPvcInfos, request.HostPathInfos)
	fmt.Printf("%+v", volumes)

	deployment.Spec.Template.Spec.Volumes = volumes
	deployment.Spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts

	deploymentInfo, err = clientSet.AppsV1().Deployments(request.NamespaceName).Update(context.TODO(), deployment, metaV1.UpdateOptions{})
	if err != nil {
		return deploymentInfo, err
	}
	//fmt.Println(err,deployment)
	return deploymentInfo, nil
}

func explainVolumeMounts(mountConfigmapInfos []MountConfigmapInfo, mountPvcInfos []MountPvcInfo, hostPathInfos []HostPathInfo) (volumeMounts []coreV1.VolumeMount, volumes []coreV1.Volume) {

	//解释configmap
	if len(mountConfigmapInfos) != 0 {
		volumeMounts1, volumes1 := explainConfigmap(mountConfigmapInfos)
		volumeMounts = append(volumeMounts, volumeMounts1...)
		volumes = append(volumes, volumes1...)
	}

	//解释目录挂载PVC
	if len(mountPvcInfos) != 0 {
		volumeMounts2, volumes2 := explainPvc(mountPvcInfos)
		volumeMounts = append(volumeMounts, volumeMounts2...)
		volumes = append(volumes, volumes2...)
	}

	//解释挂载本机目录
	if len(hostPathInfos) != 0 {
		volumeMounts3, volumes3 := explainHostPath(hostPathInfos)
		volumeMounts = append(volumeMounts, volumeMounts3...)
		volumes = append(volumes, volumes3...)
	}

	return volumeMounts, volumes
}

func RollBackDeployment(clientSet *kubernetes.Clientset, request RollBackDeploymentRequest) (deploymentInfo *appsV1.Deployment, err error) {

	deployment, err := clientSet.AppsV1().Deployments(request.NamespaceName).Get(context.TODO(), request.DeploymentName, metaV1.GetOptions{})
	if err != nil {
		return deploymentInfo, err
	}

	//修改为回滚版本的信息
	replicaSetInfo, err := GetReplicaSet(clientSet, request.NamespaceName, request.ReplicasetName)
	deployment.Spec.Template = replicaSetInfo.Spec.Template

	deploymentInfo, err = clientSet.AppsV1().Deployments(request.NamespaceName).Update(context.TODO(), deployment, metaV1.UpdateOptions{})
	if err != nil {
		return deploymentInfo, err
	}
	return deploymentInfo, nil
}
